#include "thread.h"
#include <map>
#include "cmsis_os2.h"

void TaskDispatcher(void *argument)
{
    auto thread = static_cast<Thread*>(argument);

    if(thread)
    {
        thread->PlatExec();
    }
}

Thread::Thread()
{
}

Thread::Thread(int stackSize, ThreadPriority priority)
{
    SetStackSize(stackSize);
}

Thread::~Thread()
{
    Stop();
}

void Thread::SetStackSize(int size)
{
    m_stackSize = size;
}

int Thread::GetStackSize()
{
    return m_stackSize;
}

uint32_t Thread::GetStackSizeSpace()
{
    return osThreadGetStackSpace((void*)GetThreadId());
}

void Thread::SetPriority(ThreadPriority priority)
{
    m_priority = priority;
}

Thread::ThreadPriority Thread::GetPriority()
{
    return m_priority;
}

void Thread::SetThreadName(const std::string &name)
{
    m_threadName = name;
}

std::string Thread::GetThreadName()
{
    return m_threadName;
}

uint32_t Thread::GetThreadId()
{
    return CurrentThreadId();
}

void Thread::SetFunc(const THREAD_FUNC &func)
{
    m_threadFunc = func;
}

bool Thread::IsFinished()
{
    return m_isterminate;
}

bool Thread::IsRunning()
{
    return m_isRunning;
}

void Thread::Start()
{
   Start(m_threadFunc);
}

void Thread::Start(const THREAD_FUNC &func)
{
    if(m_isterminate)
        return;
    if(m_isRunning)
        return;
    if(m_isInterruptionRequested)
        return;

    m_isRunning = true;
    m_threadFunc = func;
    CreateOsThread();
}

void Thread::Stop()
{
    if(m_isRunning && m_osThreadHandle)
    {
        osThreadTerminate(m_osThreadHandle);
        m_osThreadHandle = nullptr;
        m_isRunning = false;
    }
}

bool Thread::Wait(uint32_t timeout)
{
    if(!IsRunning())
        return true;
    return m_sem.Acquire(timeout);
}

void Thread::Join()
{
    if(m_isRunning && m_osThreadHandle)
        m_sem.Acquire();//osThreadJoin(m_osThreadHandle);
}

void Thread::RequestInterruption()
{
    if(m_osThreadHandle == nullptr)
        return;
    if(m_isterminate)
        return;
    if(!m_isRunning)
        return;
    if(m_isInterruptionRequested)
        return;
    
    m_isInterruptionRequested = true;
}

bool Thread::IsInterruptionRequested() const
{
    return m_isInterruptionRequested;
}

void Thread::PlatExec()
{
    if(m_threadFunc)
    {
        m_threadFunc();
    }
    else
    {
        OnRun();
    }

    // printf("thread exit\n");
    m_isInterruptionRequested = false;
    m_isRunning = false;
    m_isterminate = true;
    m_sem.Release();
    //the terminate
    osThreadTerminate(m_osThreadHandle);
    m_osThreadHandle = nullptr;
}

void Thread::Sleep(int ms)
{
    if(ms == 0)
        return;
    
    osDelay(ms);
}

uint32_t Thread::CurrentThreadId()
{
    return (uint32_t)(osThreadGetId());
}

std::string Thread::GetThreadName(uint32_t id)
{
    return osThreadGetName((osThreadId_t*)id);
}

uint32_t Thread::GetStackSizeSpace(uint32_t id)
{
    return osThreadGetStackSpace((void*)id);
}

int Thread::GetThreadCount()
{
    return osThreadGetCount();
}

void Thread::OnRun()
{
    //TODO
}

void Thread::CreateOsThread()
{
    if(m_osThreadHandle)
        return;
        
    osThreadAttr_t attr = {0};
    
    attr.name = m_threadName.c_str();
    attr.stack_size = m_stackSize;

    switch (m_priority)
    {
    case ThreadPriority::Lowest: 
        attr.priority = osPriorityIdle;
        break;
    case ThreadPriority::Low:   
        attr.priority = osPriorityLow;
        break;
    case ThreadPriority::Normal:
        attr.priority = osPriorityNormal;
        break;
    case ThreadPriority::High:
        attr.priority = osPriorityHigh;
        break;
    case ThreadPriority::Highest:
        attr.priority = osPriorityRealtime;
        break;
    default:
        attr.priority = osPriorityNormal;
        break;
    }

    m_osThreadHandle = osThreadNew(TaskDispatcher, this, &attr); 
}